package org.unit.testmgmt.generator;

import java.util.List;
import java.util.Map;
import java.util.Vector;
import java.util.logging.Logger;

import org.apache.commons.lang.exception.ExceptionUtils;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IProjectDescription;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.jdt.core.Flags;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.ILocalVariable;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.IParent;
import org.eclipse.jdt.core.ISourceRange;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.Signature;
import org.eclipse.jdt.core.ToolFactory;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTParser;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.formatter.CodeFormatter;
import org.eclipse.jdt.core.formatter.DefaultCodeFormatterConstants;
import org.eclipse.jdt.core.search.TypeNameMatch;
import org.eclipse.jdt.internal.corext.codemanipulation.OrganizeImportsOperation;
import org.eclipse.jdt.internal.corext.codemanipulation.OrganizeImportsOperation.IChooseImportQuery;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.Document;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.TextSelection;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TreeSelection;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.text.edits.MalformedTreeException;
import org.eclipse.text.edits.TextEdit;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.ide.IDE;
import org.unit.testmgmt.base.IUTMConstants;
import org.unit.testmgmt.base.UTMWarning;
import org.unit.testmgmt.messages.Messages;
import org.unit.testmgmt.preferences.UTMPreferences;
import org.unit.testmgmt.tml.Method;
import org.unit.testmgmt.tml.Param;

/**
 * This class provides help methods for the generation of test classes.
 * 
 * @author Robert Streng
 *
 */
@SuppressWarnings("restriction")
// TODO avoid restrictions
public class GeneratorUtils implements IUTMConstants {

	private static final String INCREMENT_SEPERATOR = "_"; //$NON-NLS-1$

	private static Logger logger = Logger.getLogger(GeneratorUtils.class.getName());

	private static ICompilationUnit lastParsedCu = null;
	private static CompilationUnit parsedCu = null;

	/**
	 * Formats the source.
	 * 
	 * @param source
	 * @return formatted source
	 */
	@SuppressWarnings({ "rawtypes" })
	public static String formatSource(String source) {

		Map options = createFormatterOptions();

		// instantiate the default code formatter with the given options
		final CodeFormatter codeFormatter = ToolFactory.createCodeFormatter(options);

		// retrieve the source to format

		final TextEdit edit = codeFormatter.format(CodeFormatter.K_COMPILATION_UNIT, // format a
																						// compilation
																						// unit
				source, // source to format
				0, // starting position
				source.length(), // length
				0, // initial indentation
				System.getProperty("line.separator") // line separator //$NON-NLS-1$
				);

		if (edit == null)
			return source;

		IDocument document = new Document(source);
		try {
			edit.apply(document);
		}
		catch (MalformedTreeException e) {
			e.printStackTrace();
		}
		catch (BadLocationException e) {
			e.printStackTrace();
		}

		return document.get();
	}

	/**
	 * @return formatter options
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	private static Map createFormatterOptions() {
		// take default Eclipse formatting options
		Map options = DefaultCodeFormatterConstants.getEclipseDefaultSettings();

		// initialize the compiler settings to be able to format 1.5 code
		options.put(JavaCore.COMPILER_COMPLIANCE, JavaCore.VERSION_1_6);
		options.put(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, JavaCore.VERSION_1_6);
		options.put(JavaCore.COMPILER_SOURCE, JavaCore.VERSION_1_6);

		// change the option to wrap each enumeration constant on a new line
		options.put(DefaultCodeFormatterConstants.FORMATTER_ALIGNMENT_FOR_ENUM_CONSTANTS,
				DefaultCodeFormatterConstants.createAlignmentValue(true,
						DefaultCodeFormatterConstants.WRAP_ONE_PER_LINE,
						DefaultCodeFormatterConstants.INDENT_ON_COLUMN));

		return options;
	}

	/**
	 * Creates a test-method-name.
	 * 
	 * @param methodName
	 * @return test-method-name
	 */
	public static String createTestMethodName(String methodName) {
		String postfix = getPostfixMethodName(); 
		return createMethodName(methodName, postfix);
	}

	protected static String getPostfixMethodName() {
		return UTMPreferences.getTestMethodPostfix();
	}
	
	/**
	 * Creates a method-name with a prefix.
	 * 
	 * @param methodName
	 * @param methodPrefix
	 * @return method-name with prefix
	 */
	public static String createMethodName(String methodName, String methodPrefix) {
		if (methodName.length() == 0)
			return methodPrefix;

		String firstLetter = String.valueOf(methodName.charAt(0)).toUpperCase();
		String restMethodeName = methodName.substring(1).toLowerCase();

		return methodPrefix + firstLetter + restMethodeName;
	}

	/**
	 * Creates a class-name.
	 * 
	 * @param className
	 * @return class-name with a upper cased first letter
	 */
	public static String createClassName(String className) {
		if (className.length() == 0)
			return ""; //$NON-NLS-1$
		String firstLetter = String.valueOf(className.charAt(0)).toUpperCase();
		String restClassName = className.substring(1).toLowerCase();

		return firstLetter + restClassName;
	}

	/**
	 * Creates a AST-root from a compilation unit.
	 * 
	 * @param cu
	 * @return CompilationUnit
	 */
	public static CompilationUnit createASTRoot(ICompilationUnit cu) {
		// creation of DOM/AST from a ICompilationUnit
		if (lastParsedCu == cu) {
			return parsedCu;
		}
		else {
			lastParsedCu = cu;
		}

		ASTParser parser = ASTParser.newParser(AST.JLS4);
		parser.setSource(cu);
		parsedCu = (CompilationUnit) parser.createAST(null);

		return parsedCu;
	}

	/**
	 * Save the file in the active editor and close it.
	 * 
	 * @param shell
	 * @param testClassName
	 */
	public static void saveAndCloseEditor(Shell shell, String testClassName) {
		IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();

		IEditorPart activeEditor = getActiveEditor();

		if (activeEditor == null)
			return;

		String title = activeEditor.getTitle();
		title = title.replace(".java", ""); //$NON-NLS-1$ //$NON-NLS-2$
		if (title.equals(testClassName)) {
			activeEditor.doSave(null);
			page.closeEditor(activeEditor, false);
		}

	}

	/**
	 * @return active editor
	 */
	public static IEditorPart getActiveEditor() {
		IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();

		return page.getActiveEditor();
	}

	/**
	 * Open the editor with the file.
	 * 
	 * @param shell
	 * @param file
	 */
	public static void openInEditor(Shell shell, final IFile file) {
		shell.getDisplay().asyncExec(new Runnable() {
			public void run() {
				IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow()
						.getActivePage();
				try {
					if (file != null) {
						IDE.openEditor(page, file, true);
					}
				}
				catch (PartInitException e) {
					logger.severe(ExceptionUtils.getStackTrace(e));
				}
			}
		});
	}

	/**
	 * Collect the compilation units from the selection.
	 * 
	 * @param selection
	 * @param fileEditorInput
	 * @return all compilationUnits in the selection
	 * @throws JavaModelException
	 * @throws UTMWarning
	 */
	public static Vector<ICompilationUnit> getCompilationUnits(ISelection selection,
			IFileEditorInput fileEditorInput) throws JavaModelException, UTMWarning {

		Object[] selectionArray;

		if (fileEditorInput != null) {
			selectionArray = new Object[1];
			selectionArray[0] = fileEditorInput;
		}
		else {
			IStructuredSelection structuredSelection;

			if (selection instanceof IStructuredSelection) {
				structuredSelection = ((IStructuredSelection) selection);
			}
			else
				return null;

			selectionArray = structuredSelection.toArray();
		}

		Vector<ICompilationUnit> selectedCompilationUnits = new Vector<ICompilationUnit>();

		for (Object tmpSelection : selectionArray) {
			if (tmpSelection instanceof IJavaElement) {
				IJavaElement javaElement = (IJavaElement) tmpSelection;
				collectCompilationUnits(javaElement, selectedCompilationUnits);
			}
			else if (tmpSelection instanceof IParent) {
				IParent parentSelection = (IParent) tmpSelection;
				IJavaElement[] javaElements = parentSelection.getChildren();

				for (IJavaElement javaElement : javaElements) {
					collectCompilationUnits(javaElement, selectedCompilationUnits);
				}
			}
			else if (tmpSelection instanceof IFileEditorInput) {
				IFileEditorInput fileEditorInputTmp = (IFileEditorInput) tmpSelection;
				IFile ifile = fileEditorInputTmp.getFile();

				if (ifile.getName().endsWith(".java")) { //$NON-NLS-1$
					ICompilationUnit cu = JavaCore.createCompilationUnitFrom(ifile);
					selectedCompilationUnits.add(cu);
				}
				else {
					throw new UTMWarning(Messages.GeneratorUtils_SelectionEnd);
				}
			}
		}

		return selectedCompilationUnits;
	}

	/**
	 * Collects the compilation units.
	 * 
	 * @param javaElements
	 * @param compilationUnits
	 * @throws JavaModelException
	 */
	private static void collectCompilationUnits(IJavaElement[] javaElements,
			Vector<ICompilationUnit> compilationUnits) throws JavaModelException {
		for (IJavaElement javaElement : javaElements) {
			collectCompilationUnits(javaElement, compilationUnits);
		}
	}

	/**
	 * Collects the compilation units.
	 * 
	 * @param javaElement
	 * @param selectedCompilationUnits
	 * @throws JavaModelException
	 */
	private static void collectCompilationUnits(IJavaElement javaElement,
			Vector<ICompilationUnit> selectedCompilationUnits) throws JavaModelException {
		switch (javaElement.getElementType()) {
		case IJavaElement.JAVA_MODEL:
			break;
		case IJavaElement.JAVA_PROJECT:
			collectCompilationUnits(((IJavaProject) javaElement).getChildren(),
					selectedCompilationUnits);
			break;
		case IJavaElement.PACKAGE_FRAGMENT_ROOT:
			collectCompilationUnits(((IPackageFragmentRoot) javaElement).getChildren(),
					selectedCompilationUnits);
			break;
		case IJavaElement.PACKAGE_FRAGMENT:
			collectCompilationUnits((IPackageFragment) javaElement, selectedCompilationUnits);
			break;
		case IJavaElement.COMPILATION_UNIT:
			selectedCompilationUnits.add((ICompilationUnit) javaElement);
			break;
		case IJavaElement.CLASS_FILE:
			break;
		default:
		}
	}

	/**
	 * Collect the compilation units.
	 * 
	 * @param pkgFrgmt
	 * @param selectedCompilationUnits
	 * @throws JavaModelException
	 */
	private static void collectCompilationUnits(IPackageFragment pkgFrgmt,
			Vector<ICompilationUnit> selectedCompilationUnits) throws JavaModelException {
		ICompilationUnit[] compilationUnits = pkgFrgmt.getCompilationUnits();
		for (ICompilationUnit cu : compilationUnits) {
			selectedCompilationUnits.add(cu);
		}
	}

	/**
	 * @param selection
	 * @param fileEditorInput
	 * @return java-project from the selection or from the editor.
	 */
	public static IJavaProject getProject(ISelection selection, IFileEditorInput fileEditorInput) {
		Object firstElement;
		IProject project = null;

		if (fileEditorInput != null) {
			firstElement = fileEditorInput;
		}
		else {
			firstElement = getFirstSelectedElement(selection);

			if (firstElement == null) {
				return null;
			}
		}

		if (firstElement instanceof IFile) {
			IFile file = (IFile) firstElement;
			project = file.getProject();
		}
		else if (firstElement instanceof IFileEditorInput) {
			IFileEditorInput fileEditorInputTmp = (IFileEditorInput) firstElement;
			project = fileEditorInputTmp.getFile().getProject();
		}
		else if (firstElement instanceof ICompilationUnit) {
			ICompilationUnit compilationUnit = (ICompilationUnit) firstElement;
			return compilationUnit.getJavaProject();
		}

		if (project == null) {
			return null;
		}

		return convertToJavaProject(project);
	}

	/**
	 * @param project
	 * @return converted java project
	 */
	private static IJavaProject convertToJavaProject(IProject project) {
		IJavaProject jProject;
		try {
			if (project.hasNature(JavaCore.NATURE_ID)) {
				jProject = JavaCore.create(project);
			}
			else {
				throw new RuntimeException(Messages.GeneratorUtils_OnlyJavaProjects);
			}
		}
		catch (CoreException e) {
			logger.warning(e.getStackTrace().toString());
			return null;
		}

		return jProject;
	}

	/**
	 * @param selection
	 * @return first element of the selection
	 */
	private static Object getFirstSelectedElement(ISelection selection) {
		if (selection instanceof TreeSelection) {
			TreeSelection treeSelection = (TreeSelection) selection;
			return treeSelection.getFirstElement();
		}
		else if (selection instanceof StructuredSelection) {
			StructuredSelection structuredSelection = (StructuredSelection) selection;
			return structuredSelection.getFirstElement();
		}
		else if (selection instanceof TextSelection) {
			return null;
		}
		else {
			throw new RuntimeException(Messages.GeneratorUtils_SelectionNotSupported);
		}
	}

	/**
	 * Creates a folder in the project.
	 * 
	 * @param jproject
	 * @param folderName
	 * @return created folder
	 */
	public static IFolder createFolder(IJavaProject jproject, String folderName) {
		// folderName = folderName.replace(".", "/");
		IFolder folder = jproject.getProject().getFolder(folderName);

		try {
			if (!folder.exists()) {
				folder.create(true, true, null);
			}

		}
		catch (Exception e) {
			logger.severe(e.getMessage());
			throw new RuntimeException(Messages.GeneratorUtils_ErrorPackageCreation);
		}

		return folder;
	}

	/**
	 * creates src- and package-folders
	 * 
	 * @param project
	 * @param name
	 * @return
	 * @throws CoreException
	 */
	public static IPackageFragment createPackage(IJavaProject javaProject, String name)
			throws CoreException {
		IFolder folder = javaProject.getProject().getFolder("src"); //$NON-NLS-1$
		if (!folder.exists()) {
			folder.create(true, true, null);
		}

		// create package fragment
		IPackageFragmentRoot parentFolder = javaProject.getPackageFragmentRoot(folder);

		IPackageFragment packageFragment = parentFolder.getPackageFragment(name);
		if (packageFragment == null || !packageFragment.exists()) {
			packageFragment = parentFolder.createPackageFragment(name, true, null);
		}

		return packageFragment;
	}

	/**
	 * creates src- and package-folders
	 * 
	 * @param project
	 * @param name
	 * @return
	 * @throws CoreException
	 */
	public static IPackageFragment createPackage(IJavaProject javaProject, String parentFolderName,
			String name) throws CoreException {
		IFolder folder = javaProject.getProject().getFolder(parentFolderName);
		if (!folder.exists()) {
			folder.create(true, true, null);
		}

		// create package fragment
		IPackageFragmentRoot parentFolder = javaProject.getPackageFragmentRoot(folder);

		IPackageFragment packageFragment = parentFolder.getPackageFragment(name);
		if (packageFragment == null || !packageFragment.exists()) {
			packageFragment = parentFolder.createPackageFragment(name, true, null);
		}

		return packageFragment;
	}

	/**
	 * Creates a java project.
	 * 
	 * @param name
	 * @param baseProject
	 * @return the created java project
	 * @throws CoreException
	 */
	public static IJavaProject createProject(String name, IJavaProject baseProject)
			throws CoreException {
		IWorkspace workspace = ResourcesPlugin.getWorkspace();
		IWorkspaceRoot root = workspace.getRoot();

		IProject project = root.getProject(name);

		if (project.exists())
			return JavaCore.create(project);

		// create if not exists
		project.create(null);
		project.open(null);

		// transfer the project-nature
		IProjectDescription description = project.getDescription();
		
		
		description.setReferencedProjects(getDefaultReferencedProjects(root));
		project.setDescription(description, null);

		// create Java-Project
		IJavaProject javaProject = JavaCore.create(project);

		if (baseProject != null) {
			description.setNatureIds(baseProject.getProject().getDescription().getNatureIds());
			
			Vector<IClasspathEntry> buildPath = new Vector<IClasspathEntry>();
			for (IClasspathEntry entry : baseProject.getRawClasspath()) {
				if (!(entry.getEntryKind() == IClasspathEntry.CPE_SOURCE))
					buildPath.add(entry);
			}

			// add source-path
			buildPath.add(JavaCore.newSourceEntry(javaProject.getPath().append("src"))); //$NON-NLS-1$
			// add base-project-path
			buildPath.add(JavaCore.newProjectEntry(baseProject.getPath()));
			
			// set the path
			IClasspathEntry[] entries = new IClasspathEntry[buildPath.size()];
			entries = buildPath.toArray(entries);
			javaProject.setRawClasspath(entries, null);
		}

		return javaProject;
	}

	/**
	 * Returns the default projects.
	 * 
	 * @param root
	 * @return the default projects.
	 */
	private static IProject[] getDefaultReferencedProjects(IWorkspaceRoot root) {
		IProject[] projects = new IProject[1];
		projects[0] = root.getProject("org.unit.testmgmt"); //$NON-NLS-1$
		return projects;
	}


	/**
	 * Changes the first character to upper.
	 * 
	 * @param value
	 * @return String with a upper first character
	 */
	public static String firstCharToUpper(String value) {
		if (value != null && value.length() > 0)
			return Character.toUpperCase(value.charAt(0)) + value.substring(1);

		return value;
	}

	/**
	 * Changes the first character to lower.
	 * 
	 * @param value
	 * @return String with a lower first character
	 */
	public static String firstCharToLower(String value) {
		if (value != null && value.length() > 0)
			return Character.toLowerCase(value.charAt(0)) + value.substring(1);

		return value;
	}

	/**
	 * Returns the java project.
	 * 
	 * @param cu
	 * @return java project
	 */
	public static IJavaProject getProject(ICompilationUnit cu) {
		IJavaElement javaElement = cu.getAncestor(IJavaElement.JAVA_PROJECT);
		if (javaElement == null)
			return null;

		return (IJavaProject) javaElement;
	}
	
	/**
	 * Returns the java project.
	 * 
	 * @param name
	 * @return java project
	 */
	public static IJavaProject getProject(String name) {
		return getProject(name, false, null);
	}

	/**
	 * Gets or creates a java project.
	 * 
	 * @param name
	 * @param createIfNotExists
	 * @param baseProject
	 * @return the java project
	 */
	public static IJavaProject getProject(String name, boolean createIfNotExists,
			IJavaProject baseProject) {
		for (IProject project : getProjects()) {
			if (project.getName().equals(name)) {
				return convertToJavaProject(project);
			}
		}

		// create project
		if (createIfNotExists) {
			try {
				return createProject(name, baseProject);
			}
			catch (CoreException e) {
				throw new RuntimeException(e);
			}
		}

		return null;
	}

	/**
	 * Returns the projects from the workspace.
	 * 
	 * @return projects
	 */
	public static IProject[] getProjects() {
		IProject[] projects = ResourcesPlugin.getWorkspace().getRoot().getProjects();

		return projects;
	}

	/**
	 * Returns the package.
	 * 
	 * @param cu
	 * @return the package
	 */
	public static IPackageFragment getPackage(ICompilationUnit cu) {
		IJavaElement tmp = cu.getAncestor(IJavaElement.PACKAGE_FRAGMENT);

		if (tmp == null)
			return null;

		return (IPackageFragment) tmp;
	}

	/**
	 * Creates a method.
	 * 
	 * @param type
	 * @param modifier
	 * @param returnType
	 * @param methodName
	 * @param throwsClause
	 * @param params
	 * @param body
	 * @param annotations
	 * @return the created method
	 * @throws JavaModelException
	 */
	public static IMethod createMethod(IType type, String modifier, String returnType,
			String methodName, String throwsClause, String params, String body,
			String... annotations) throws JavaModelException {
		return createMethod(type, modifier, returnType, methodName, throwsClause, params, body,
				true, annotations);
	}

	/**
	 * Compares the methods.
	 * 
	 * @param method
	 * @param tmlMethod
	 * @return true if the methods are equal
	 */
	public static boolean compareMethods(IMethod method, Method tmlMethod) {
		List<Param> tmlParams;
		ILocalVariable[] parameters;
		String paramType;

		if (tmlMethod == null) {
			if (method == null)
				return true;

			return false;
		}

		if (tmlMethod.getName() == null)
			return false;

		if (tmlMethod.getName().equals(method.getElementName())) {
			tmlParams = tmlMethod.getParam();
			try {
				parameters = method.getParameters();
			}
			catch (JavaModelException e) {
				throw new RuntimeException(e);
			}

			// check parameters
			if (tmlParams.size() == parameters.length) {
				for (int i = 0; i < tmlParams.size(); i++) {
					paramType = Signature.getSignatureSimpleName(parameters[i].getTypeSignature());
					if (!tmlParams.get(i).getType().equals(paramType)) {
						return false;
					}
				}

				return true;
			}

		}

		return false;
	}

	/**
	 * Returns if the modifiers are equal.
	 * @param method
	 * @param modifier
	 * @return true if the modifiers are equal
	 */
	public static boolean isMethodModifierEqual(IMethod method, String modifier) {
		String methodModifier;
		try {
			methodModifier = getMethodModifier(method);
		}
		catch (JavaModelException e) {
			return false;
		}

		if (methodModifier.equals(modifier)) {
			return true;
		}

		return false;
	}

	/**
	 * Creates a method.
	 * 
	 * @param type
	 * @param modifier
	 * @param returnType
	 * @param methodName
	 * @param throwsClause
	 * @param params
	 * @param body
	 * @param increment
	 * @param annotations
	 * @return the created method
	 * @throws JavaModelException
	 */
	public static IMethod createMethod(IType type, String modifier, String returnType,
			String methodName, String throwsClause, String params, String body, boolean increment,
			String... annotations) throws JavaModelException {

		IMethod tmpMethod = type.getMethod(methodName, null);
		StringBuilder sbMethod = new StringBuilder();

		if (increment) {
			while (tmpMethod.exists()) {
				methodName = incrementTestMethodName(methodName, "_"); //$NON-NLS-1$
				tmpMethod = type.getMethod(methodName, null);
			}
		}

		if (tmpMethod.exists()) {
			logger.fine(Messages.GeneratorUtils_MethodExists + methodName);
			return tmpMethod;
		}

		if (annotations != null) {
			for (String annotation : annotations) {
				if (!annotation.startsWith("@")) { //$NON-NLS-1$
					annotation = "@" + annotation; //$NON-NLS-1$
				}

				sbMethod.append(annotation);
				sbMethod.append("\n"); //$NON-NLS-1$
			}
		}

		if (throwsClause != null && throwsClause.length() > 0) {
			throwsClause = "throws " + throwsClause; //$NON-NLS-1$
		}
		else
			throwsClause = ""; //$NON-NLS-1$

		if (returnType == null)
			returnType = ""; //$NON-NLS-1$

		if (params == null)
			params = ""; //$NON-NLS-1$

		sbMethod.append(modifier).append(" ").append(returnType).append(" ").append(methodName) //$NON-NLS-1$ //$NON-NLS-2$
				.append("(").append(params).append(") " + throwsClause + " {\n").append(body) //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
				.append("\n}"); //$NON-NLS-1$

		try {
			tmpMethod = type.createMethod(sbMethod.toString(), null, false, null);
		}
		catch (Exception ex) {
			logger.severe(ExceptionUtils.getFullStackTrace(ex));
		}

		return tmpMethod;
	}

	/**
	 * Increments a method name.
	 * 
	 * @param methodName
	 * @param incrementSeperator
	 * @return incremented method name
	 */
	public static String incrementTestMethodName(String methodName, String incrementSeperator) {
		if (incrementSeperator == null) {
			incrementSeperator = INCREMENT_SEPERATOR;
		}

		String lastLetter = ""; //$NON-NLS-1$
		int methodNameLength = methodName.length();
		int lastLetterIndex = methodNameLength - 1;
		for (int i = lastLetterIndex; i >= 0; i--) {
			lastLetter = methodName.substring(i, i + 1);

			try {
				Integer.parseInt(lastLetter);
			}
			catch (NumberFormatException e) {
				lastLetterIndex = i;
				break;
			}
		}

		if (lastLetter.equals(incrementSeperator)) {
			int firstNumberIndex = lastLetterIndex + 1;
			int methodNamePostfixnumber;
			if (firstNumberIndex < methodNameLength) {
				methodNamePostfixnumber = Integer.parseInt(methodName.substring(
						lastLetterIndex + 1, methodNameLength));
			}
			else {
				methodNamePostfixnumber = 0;
			}
			methodNamePostfixnumber++;

			methodName = methodName.substring(0, lastLetterIndex + 1) + methodNamePostfixnumber;
		}
		else {
			// first increment
			methodName += "_1"; //$NON-NLS-1$
		}

		return methodName;
	}
	
	/**
	 * Returns the method modifier as String.
	 * @param method
	 * @return method modifier
	 * @throws JavaModelException
	 */
	public static String getMethodModifier(IMethod method) throws JavaModelException {
		int methodFlags = method.getFlags();

		if (Flags.isPublic(methodFlags)) {
			return MOD_PUBLIC;
		}
		else if (Flags.isProtected(methodFlags)) {
			return MOD_PROTECTED;
		}
		else if (Flags.isPrivate(methodFlags)) {
			return MOD_PRIVATE;
		}
		else if (Flags.isPackageDefault(methodFlags)) {
			return MOD_PACKAGE;
		}

		return ""; //$NON-NLS-1$
	}

	/**
	 * Searches the closest method. 
	 * 
	 * @param method
	 * @param tmlMethods
	 * @return closest method
	 */
	public static Method getClosestMethod(IMethod method, List<Method> tmlMethods) {
		Method tmlMethodTmp = null;
		for (Method tmlMethod : tmlMethods) {
			if (compareMethods(method, tmlMethod)) {
				if (isMethodModifierEqual(method, tmlMethod.getModifier())) {
					return tmlMethod;
				}
				tmlMethodTmp = tmlMethod;
			}
		}

		return tmlMethodTmp;
	}
	
	/**
	 * Organize the imports of a compilation unit.
	 * 
	 * @param testCompilationUnit
	 * @return
	 * @throws OperationCanceledException
	 * @throws CoreException
	 */
	public static String organizeImports(ICompilationUnit testCompilationUnit)
			throws OperationCanceledException, CoreException {
		// TODO change the restricted action with the commented
		// OrganizeImportsAction importAction = new OrganizeImportsAction(site);
		// importAction.run(testCompilationUnit);

		CompilationUnit astRoot = createASTRoot(testCompilationUnit);
		Document document = new Document(testCompilationUnit.getSource());

		OrganizeImportsOperation oio = new OrganizeImportsOperation(testCompilationUnit, astRoot,
				true, true, true, new IChooseImportQuery() {

					@Override
					public TypeNameMatch[] chooseImports(TypeNameMatch[][] openChoices,
							ISourceRange[] ranges) {
						return openChoices[0];
					}
				});

		TextEdit te = oio.createTextEdit(null);
		try {
			te.apply(document);
		}
		catch (MalformedTreeException e) {
			e.printStackTrace();
		}
		catch (BadLocationException e) {
			e.printStackTrace();
		}

		return document.get();
	}
}
